go通道channel 死锁的场景和原因浅析 |
您所在的位置:网站首页 › go 协程 channel › go通道channel 死锁的场景和原因浅析 |
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情 通道简介go的通道channel是用于协程之间数据通信的一种方式,因为协程goroutine在运行的时候不保证顺序,且是并发(非并行)执行,数据的传递要么通过共享内存(比如指针)传递,要么就是通过一个额外的channel来传递。 其实推荐使用channel来传递数据主要是: 1.避免协程竞争和数据冲突问题,大家都去抢那个共享内存的指针是有冲突的,同时修改又有并发问题,还需要加锁 2.更高级抽象,降低开发难度,管道通信是一种通信方式,只需要监听即可,无需多次轮训来耗费资源 3.模块之间更容易解耦,增加扩展性和可维护性,通道有时候更像是生产者消费者模式,产生数据的协程和接受数据的协程无需知晓对方的存在,只专注自己的事就好 通道长啥样 type hchan struct { qcount uint dataqsiz uint buf unsafe.Pointer elemsize uint16 closed uint32 elemtype *_type sendx uint recvx uint recvq waitq sendq waitq lock mutex } 复制代码 qcount channel 中的元素个数 dataqsiz channel 中循环缓存队列的长度 buf channel中缓存区的指针 elemsize channel收发(元素)的大小 elemtype channel收发(元素)的类型 closed 通道关闭的flag,一旦关闭将不能接受新的消息 sendx sendq,发送队列的指针和发送队列 recvx recvq ,接收队列的指针和接收队列 lock 锁,保护整个结构体通道channel主要是设置了一个环状的缓冲区,其实就是个环形链表,好处就是降低gc的开销,缓冲区的大小是创建channel的时候传入的。当没有设置缓冲区的时候,缓冲区为0,这时候称该通道为无缓冲通道,反之就是有缓冲的通道。 关于两者的区别主要是在阻塞的过程上有差别。 对于无缓冲区的channel 发送数据方在发送的时候,如果没有接收者,那么发送方的协程groutine阻塞在通道的sendq里面。 接收数据方会一直等待数据到来,如果数据一直没有到来,那么接收方的协程groutine就会阻塞在channel的recvq里面。 对于有缓冲区的channel 在缓冲区没有满的时候,发送方只管发送,当缓冲区存满的时候,发送方的协程groutine就会阻塞,如果这时候有协程过来取数据,那么优先给他缓存里的数据,然后再把sendq里面的协程的数据copy到缓存,并把该协程从阻塞中释放出来,也就是唤醒。 当缓冲区没有任何数据,也就是管道里面没有数据的时候,接收方就因为取不到数据而阻塞,进入到recvq里面等待数据到来。当数据到的时候,发送方的协程直接把数据cpoy给接收方的协程,不需额外的经过一次缓冲区。 死锁场景1. 没有缓冲区的时候,单协程内通道同时写和读 func main() { // 创建一个通道 ch := make(chan int) ch |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |